import { t } from '@/locales/i18n';
import {
  ChatRoleEnum,
  defaultChatInputGuideConfig,
  defaultTTSConfig,
  defaultWhisperConfig,
  RuntimeUserPromptType,
  UserChatItemType,
} from '@/pages/chat/tools/constants';
import { getNanoid } from '@/pages/chat/tools/tools';
import { runtimePrompt2ChatsValue } from '@/utils/chat/adapt';

import { getPluginRunContent } from '../app/plugin/utils';
import {
  AppChatConfigType,
  AppScheduledTriggerConfigType,
  AppTTSConfigType,
  AppWhisperConfigType,
  ChatInputGuideConfigType,
  VariableItemType,
} from '../app/type';

import { EditorVariablePickerType } from './components/common/Textarea/PromptEditor/type';
import {
  chatHistoryValueDesc,
  FlowNodeInputTypeEnum,
  FlowNodeOutputTypeEnum,
  FlowNodeTypeEnum,
  NodeInputKeyEnum,
  NodeOutputKeyEnum,
  VARIABLE_NODE_ID,
  VariableInputEnum,
  variableMap,
  WorkflowIOValueTypeEnum,
} from './constant';
import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './io';
import { StoreNodeItemType } from './node';
import { RuntimeNodeItemType } from './runtime/type';
import { getReferenceVariableValue } from './runtime/utils';
import {
  Input_Template_History,
  Input_Template_Stream_MODE,
  Input_Template_UserChatInput,
} from './template/input';
import { IfElseResultEnum } from './template/system/ifElse/constant';
import { SelectOptionItemType } from './template/system/interactive/type';

export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
  return `${nodeId}-${type}-${key}`;
};

export const checkInputIsReference = (input: FlowNodeInputItemType) => {
  if (input.renderTypeList?.[input?.selectedTypeIndex || 0] === FlowNodeInputTypeEnum.reference)
    return true;

  return false;
};

/* node  */
export const getGuideModule = (modules: StoreNodeItemType[]) =>
  modules.find(
    (item) =>
      item.flowNodeType === FlowNodeTypeEnum.systemConfig ||
      // @ts-ignore (adapt v1)
      item.flowType === FlowNodeTypeEnum.systemConfig,
  );
export const splitGuideModule = (guideModules?: StoreNodeItemType) => {
  const welcomeText: string =
    guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.welcomeText)?.value || '';

  const variables: VariableItemType[] =
    guideModules?.inputs.find((item) => item.key === NodeInputKeyEnum.variables)?.value || [];

  const questionGuide: boolean =
    !!guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.questionGuide)?.value ||
    false;

  const ttsConfig: AppTTSConfigType =
    guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.tts)?.value ||
    defaultTTSConfig;

  const whisperConfig: AppWhisperConfigType =
    guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.whisper)?.value ||
    defaultWhisperConfig;

  const scheduledTriggerConfig: AppScheduledTriggerConfigType = guideModules?.inputs?.find(
    (item) => item.key === NodeInputKeyEnum.scheduleTrigger,
  )?.value;

  const chatInputGuide: ChatInputGuideConfigType =
    guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.chatInputGuide)?.value ||
    defaultChatInputGuideConfig;

  // plugin
  const instruction: string =
    guideModules?.inputs?.find((item) => item.key === NodeInputKeyEnum.instruction)?.value || '';

  return {
    welcomeText,
    variables,
    questionGuide,
    ttsConfig,
    whisperConfig,
    scheduledTriggerConfig,
    chatInputGuide,
    instruction,
  };
};

// Get app chat config: db > nodes
export const getAppChatConfig = ({
  chatConfig,
  systemConfigNode,
  storeVariables,
  storeWelcomeText,
  isPublicFetch = false,
}: {
  chatConfig?: AppChatConfigType;
  systemConfigNode?: StoreNodeItemType;
  storeVariables?: VariableItemType[];
  storeWelcomeText?: string;
  isPublicFetch: boolean;
}): AppChatConfigType => {
  const {
    welcomeText,
    variables,
    questionGuide,
    ttsConfig,
    whisperConfig,
    scheduledTriggerConfig,
    chatInputGuide,
    instruction,
  } = splitGuideModule(systemConfigNode);

  const config: AppChatConfigType = {
    questionGuide,
    ttsConfig,
    whisperConfig,
    scheduledTriggerConfig,
    chatInputGuide,
    instruction,
    ...chatConfig,
    variables: storeVariables ?? chatConfig?.variables ?? variables,
    welcomeText: storeWelcomeText ?? chatConfig?.welcomeText ?? welcomeText,
  };

  if (!isPublicFetch) {
    config.scheduledTriggerConfig = undefined;
  }

  return config;
};

export const getOrInitModuleInputValue = (input: FlowNodeInputItemType) => {
  if (input.value !== undefined || !input.valueType) return input.value;
  if (input.defaultValue !== undefined) return input.defaultValue;

  const map: Record<string, any> = {
    [WorkflowIOValueTypeEnum.boolean]: false,
    [WorkflowIOValueTypeEnum.number]: 0,
    [WorkflowIOValueTypeEnum.string]: '',
  };

  return map[input.valueType];
};

export const getModuleInputUiField = (input: FlowNodeInputItemType) => {
  // if (input.renderTypeList === FlowNodeInputTypeEnum.input || input.type === FlowNodeInputTypeEnum.textarea) {
  //   return {
  //     placeholder: input.placeholder || input.description
  //   };
  // }
  return {};
};

export const pluginData2FlowNodeIO = ({
  nodes,
}: {
  nodes: StoreNodeItemType[];
}): {
  inputs: FlowNodeInputItemType[];
  outputs: FlowNodeOutputItemType[];
} => {
  const pluginInput = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
  const pluginOutput = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginOutput);

  return {
    inputs: pluginInput
      ? [
          Input_Template_Stream_MODE,
          ...pluginInput?.inputs.map((item) => ({
            ...item,
            ...getModuleInputUiField(item),
            value: getOrInitModuleInputValue(item),
            canEdit: false,
            renderTypeList:
              item.renderTypeList[0] === FlowNodeInputTypeEnum.customVariable
                ? [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input]
                : item.renderTypeList,
          })),
        ]
      : [],
    outputs: pluginOutput
      ? [
          ...pluginOutput.inputs.map((item) => ({
            id: item.key,
            type: FlowNodeOutputTypeEnum.static,
            key: item.key,
            valueType: item.valueType,
            label: item.label || item.key,
            description: item.description,
          })),
        ]
      : [],
  };
};

export const appData2FlowNodeIO = ({
  chatConfig,
}: {
  chatConfig?: AppChatConfigType;
}): {
  inputs: FlowNodeInputItemType[];
  outputs: FlowNodeOutputItemType[];
} => {
  const variableInput = !chatConfig?.variables
    ? []
    : chatConfig.variables.map((item) => {
        const renderTypeMap = {
          [VariableInputEnum.input]: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
          [VariableInputEnum.textarea]: [
            FlowNodeInputTypeEnum.textarea,
            FlowNodeInputTypeEnum.reference,
          ],
          [VariableInputEnum.numberInput]: [FlowNodeInputTypeEnum.numberInput],
          [VariableInputEnum.select]: [FlowNodeInputTypeEnum.select],
          [VariableInputEnum.custom]: [
            FlowNodeInputTypeEnum.input,
            FlowNodeInputTypeEnum.reference,
          ],
          default: [FlowNodeInputTypeEnum.reference],
        };

        return {
          key: item.key,
          renderTypeList: renderTypeMap[item.type] || renderTypeMap.default,
          label: item.label,
          debugLabel: item.label,
          description: '',
          valueType: WorkflowIOValueTypeEnum.any,
          required: item.required,
          list: item.enums?.map((enumItem) => ({
            label: enumItem.value,
            value: enumItem.value,
          })),
        };
      });

  // const showFileLink =
  //   chatConfig?.fileSelectConfig?.canSelectFile || chatConfig?.fileSelectConfig?.canSelectImg;

  return {
    inputs: [
      Input_Template_Stream_MODE,
      Input_Template_History,
      Input_Template_UserChatInput,
      // ...(showFileLink ? [Input_Template_File_Link] : []),
      ...variableInput,
    ],
    outputs: [
      {
        id: NodeOutputKeyEnum.history,
        key: NodeOutputKeyEnum.history,
        required: true,
        label: t('core.module.output.label.New context'),
        description: t('core.module.output.description.New context'),
        valueType: WorkflowIOValueTypeEnum.chatHistory,
        valueDesc: chatHistoryValueDesc,
        type: FlowNodeOutputTypeEnum.static,
      },
      {
        id: NodeOutputKeyEnum.answerText,
        key: NodeOutputKeyEnum.answerText,
        required: false,
        label: t('core.module.output.label.Ai response content'),
        description: t('core.module.output.description.Ai response content'),
        valueType: WorkflowIOValueTypeEnum.string,
        type: FlowNodeOutputTypeEnum.static,
      },
    ],
  };
};

export const formatEditorVariablePickerIcon = (
  variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[],
): EditorVariablePickerType[] => {
  return variables.map((item) => ({
    ...item,
    icon: item.type ? variableMap[item.type]?.icon : variableMap.input.icon,
  }));
};

export const isReferenceValue = (value: any, nodeIds: string[]): boolean => {
  const validIdList = [VARIABLE_NODE_ID, ...nodeIds];
  return Array.isArray(value) && value.length === 2 && validIdList.includes(value[0]);
};

export const getElseIFLabel = (i: number) => {
  return i === 0 ? IfElseResultEnum.IF : `${IfElseResultEnum.ELSE_IF} ${i}`;
};

// add value to plugin input node when run plugin
export const updatePluginInputByVariables = (
  nodes: RuntimeNodeItemType[],
  variables: Record<string, any>,
) => {
  return nodes.map((node) =>
    node.flowNodeType === FlowNodeTypeEnum.pluginInput
      ? {
          ...node,
          inputs: node.inputs.map((input) => {
            const parseValue = (() => {
              try {
                if (
                  input.valueType === WorkflowIOValueTypeEnum.string ||
                  input.valueType === WorkflowIOValueTypeEnum.number ||
                  input.valueType === WorkflowIOValueTypeEnum.boolean
                )
                  return variables[input.key];

                return JSON.parse(variables[input.key]);
              } catch (e) {
                return variables[input.key];
              }
            })();

            return {
              ...input,
              value: parseValue ?? input.value,
            };
          }),
        }
      : node,
  );
};

// replace {{$xx.xx$}} variables for text
export function replaceEditorVariable({
  text,
  nodes,
  variables,
  runningNode,
}: {
  text: any;
  nodes: RuntimeNodeItemType[];
  variables: Record<string, any>; // global variables
  runningNode: RuntimeNodeItemType;
}) {
  if (typeof text !== 'string') return text;

  const globalVariables = Object.keys(variables).map((key) => {
    return {
      nodeId: VARIABLE_NODE_ID,
      id: key,
      value: variables[key],
    };
  });

  // Upstream node outputs
  const nodeVariables = nodes
    .map((node) => {
      return node.outputs.map((output) => {
        return {
          nodeId: node.nodeId,
          id: output.id,
          value: output.value,
        };
      });
    })
    .flat();

  // Get runningNode inputs(Will be replaced with reference)
  const customInputs = runningNode.inputs.flatMap((item) => {
    if (Array.isArray(item.value)) {
      return [
        {
          id: item.key,
          value: getReferenceVariableValue({
            value: item.value as ReferenceValueProps,
            nodes,
            variables,
          }),
          nodeId: runningNode.nodeId,
        },
      ];
    }
    return [
      {
        id: item.key,
        value: item.value,
        nodeId: runningNode.nodeId,
      },
    ];
  });

  const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];

  // Replace {{$xxx.xxx$}} to value
  for (const key in allVariables) {
    const variable = allVariables[key];
    const val = variable.value;
    const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);

    const regex = new RegExp(`\\{\\{\\$(${variable.nodeId}\\.${variable.id})\\$\\}\\}`, 'g');
    text = text.replace(regex, formatVal);
  }
  return text || '';
}

/* Get plugin runtime input user query */
export const getPluginRunUserQuery = ({
  pluginInputs,
  variables,
  files = [],
}: {
  pluginInputs: FlowNodeInputItemType[];
  variables: Record<string, any>;
  files?: RuntimeUserPromptType['files'];
}): UserChatItemType & { dataId: string } => {
  return {
    dataId: getNanoid(24),
    obj: ChatRoleEnum.Human,
    value: runtimePrompt2ChatsValue({
      text: getPluginRunContent({
        pluginInputs,
        variables,
      }),
      files,
    }),
  };
};

export function buildTree(paths: string[][]): SelectOptionItemType[] {
  const root: SelectOptionItemType[] = [];

  paths.forEach((path) => {
    let currentLevel = root;

    path.forEach((segment, index) => {
      const existingNode = currentLevel.find((node) => node.value === segment);

      if (existingNode) {
        if (!existingNode.children && index < path.length - 1) {
          existingNode.children = [];
        }
        currentLevel = existingNode.children || [];
      } else {
        const newNode: SelectOptionItemType = {
          value: segment,
          label: segment,
        };

        if (index < path.length - 1) {
          newNode.children = [];
        }

        currentLevel.push(newNode);
        currentLevel = newNode.children || [];
      }
    });
  });

  return root;
}

// 将级联选项转换回文本的函数（新增）
export function cascaderOptionsToText(
  options: SelectOptionItemType[] = [],
  parentPath: string[] = [],
): string[] {
  try {
    let result: string[] = [];

    console.log('cascaderOptionsToText ', options);

    if (Array.isArray(options)) {
      options.forEach((option) => {
        const currentPath = [...parentPath, option.label];

        if (!option.children || option.children.length === 0) {
          result.push(currentPath.join(','));
        }

        if (option.children && option.children.length > 0) {
          result = result.concat(cascaderOptionsToText(option.children, currentPath));
        }
      });
    }

    return result;
  } catch (error) {
    console.error('Parse cascader options error:', error);
    return [];
  }
}

export const getIcon = (valueType: WorkflowIOValueTypeEnum | undefined) => {
  switch (valueType) {
    case WorkflowIOValueTypeEnum.string:
      return 'String';
    case WorkflowIOValueTypeEnum.boolean:
      return 'typeof-bool';
    case WorkflowIOValueTypeEnum.number:
      return 'typeof-int';
    case WorkflowIOValueTypeEnum.object:
      return 'typeof-obj';
    case WorkflowIOValueTypeEnum.chatHistory:
      return 'history';
    case WorkflowIOValueTypeEnum.datasetQuote:
      return 'ic_setting_dataset';
    case WorkflowIOValueTypeEnum.arrayAny:
    case WorkflowIOValueTypeEnum.arrayBoolean:
    case WorkflowIOValueTypeEnum.arrayNumber:
    case WorkflowIOValueTypeEnum.arrayObject:
    case WorkflowIOValueTypeEnum.arrayString:
      return 'typeof-array';
    default:
      return 'json';
  }
};
